package eu.dzhw.fdz;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.time.Instant;
import java.time.LocalDateTime;

/**
 * Generates an Angular-Module for constants.<br>
 * This goal is a port of a Grunt task that used to be part of the build pipeline.
 * @see <a href="https://github.com/dzhw/metadatamanagement/blob/b1d937b16a6c00ec67aa73c31f29e30d3c3f020b/Gruntfile.js#L645">Local profile config for ngconstants</a>
 * @since Aug 2023
 * @author <a href="mailto:tvillwock@codematix.de">Tilo Villwock</a>
 */
@Mojo(name = "generate-frontend-constants", defaultPhase = LifecyclePhase.GENERATE_SOURCES)
public class GenerateFrontendConstantsMojo extends AbstractMojo {

    /**
     * A data class to map the Elasticsearch-Client part
     * of the profile specific application.yml file.
     */
    static class ClientConfigNode {
        public String apiVersion;
        public String logLevel;
        public Integer pageSize;
    }

    /**
     * A data class to map the Metadatamanagement-App part
     * of the profile specific application.yml file.
     */
    static class MDMConfigNode {
        @JsonProperty("elasticsearch-angular-client")
        public ClientConfigNode client;
    }

    /**
     * A data class to map part of the profile
     * specific application.yml file.
     */
    static class AppConfig {
        public MDMConfigNode metadatamanagement;
    }

    /** The template for the Angular-Module. */
    private static final String TEMPLATE = """
        // jscs:disable
        'use strict';
        // generated by mdm:generate-frontend-constants on %%TIMESTAMP%%
        angular.module('metadatamanagementApp')
          .constant('ENV', '%%PROFILE%%')
          .constant('VERSION', '%%VERSION%%')
          .constant('ElasticSearchProperties', {
            apiVersion: '%%ES_API_VERSION%%',
            logLevel: '%%ES_LOG_LEVEL%%',
            pageSize: %%ES_PAGE_SIZE%%
          });
        """;

    /** The project version. */
    @Parameter(property = "project.version")
    private String version;

    /** The active build profile. */
    @Parameter(property = "project.activeProfiles[0].id")
    private String profile;

    /**
     * The destination where the Angular module file will be written to. This
     * parameter has a default value and can be overriden via the commandline:
     * <pre>
     *     mvn mdm:generate-frontend-constants -Dmdm.output=/path/to/constants-file
     * </pre>
     */
    @Parameter(property = "mdm.output", defaultValue = "./mdm-frontend/src/app/legacy/app.constants.js")
    private String output;

    @Override
    public void execute() throws MojoExecutionException, MojoFailureException {

        // create YAML object mapper and configure it to ignore
        // properties that are missing from the data classes
        final ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        // parse profile specific application.yml configuration file
        final String filepath = String.format("./src/main/resources/config/application-%s.yml",
            this.profile.equals("local-minified") ? "local" : this.profile);
        AppConfig appConfig = null;
        try {
            appConfig = mapper.readValue(new File(filepath), AppConfig.class);
        } catch (IOException e) {
            throw new MojoExecutionException(String.format("Unable to parse YAML configuration file at '%s'", filepath), e);
        }

        // replace placeholders and write constants file to destination
        final String content = TEMPLATE
            .replace("%%TIMESTAMP%%", LocalDateTime.now().toString())
            .replace("%%PROFILE%%", this.profile)
            .replace("%%VERSION%%", this.version)
            .replace("%%ES_API_VERSION%%", appConfig.metadatamanagement.client.apiVersion)
            .replace("%%ES_LOG_LEVEL%%", appConfig.metadatamanagement.client.logLevel)
            .replace("%%ES_PAGE_SIZE%%", appConfig.metadatamanagement.client.pageSize.toString());
        try {
            Files.writeString(new File(this.output).toPath(), content);
            super.getLog().info(String.format("✔ %s", this.output));
        } catch (IOException e) {
            throw new MojoExecutionException("Unable to write file: " + filepath, e);
        }
    }
}
